//    Copyright (c) 2009 Elwyn Malethan
//
//    This file is part of SeemoreJ.
//
//    SeemoreJ is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    SeemoreJ is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.

//    You should have received a copy of the GNU General Public License
//    along with SeemoreJ.  If not, see <http://www.gnu.org/licenses/>.

package com.malethan.seemorej.taglib.elements;

import com.malethan.seemorej.taglib.Util;

import java.util.Map;
import java.util.HashMap;
import java.lang.reflect.Field;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public abstract class BaseElement {
    //----------------------------------------------------------------------- Static Properties and Constants

    private static final Log log = LogFactory.getLog(BaseElement.class);

    public static final String[] DEFAULT_ATTRIBUTES = new String[] {
            "htmlId", "htmlClass", "name"
    };
    public static final Map<String,String> ATTR_TRANSLATION = new HashMap<String, String>() {{
        put("htmlId", "id");
        put("htmlClass", "class");
    }};

    //----------------------------------------------------------------------- Static Methods

    public static StringBuffer appendAttributes(StringBuffer html, Object source, String... customAttrs) {
        String[] attributes = new String[customAttrs.length + DEFAULT_ATTRIBUTES.length];
        System.arraycopy(DEFAULT_ATTRIBUTES, 0, attributes, 0, DEFAULT_ATTRIBUTES.length);
        System.arraycopy(customAttrs, 0, attributes, DEFAULT_ATTRIBUTES.length, customAttrs.length);

        for (String attr : attributes) {
            try {
                if (source instanceof BaseElement) {
                    Class clazz = source.getClass();
                    Field field = null;
                    boolean keepChecking = true;
                    while(keepChecking) {
                        try {
                            //We don't want to check higher up than ElementTag
                            keepChecking = !clazz.equals(BaseElement.class);
                            field = clazz.getDeclaredField(attr);

                            //If we got this far, we don't want to check any further
                            keepChecking = false;
                        } catch (NoSuchFieldException e) {
                            log.warn(clazz.getName() + " has no field " + attr + " checking superclass");
                            clazz = clazz.getSuperclass();
                        }
                    }
                    String value = (String) field.get(source);
                    String name = ATTR_TRANSLATION.get(attr) != null ? ATTR_TRANSLATION.get(attr) : attr;
                    Util.appendAttribute(html, name, value);
                }
            } catch (IllegalAccessException e) {
                log.error(source.getClass().getName() + " does not have access to it's own field " + attr, e);
            } catch (ClassCastException e) {
                log.error(source.getClass().getName() + " has a field " + attr + " that isn't a string", e);
            }
        }
        return html;
    }

    //----------------------------------------------------------------------- Instance Properties

    String elementName;
    String htmlId;
    String htmlClass;
    String name;
    Object value;

    //----------------------------------------------------------------------- Constructors

    protected BaseElement(String elementName) {
        this.elementName = elementName;
    }

    //----------------------------------------------------------------------- Getters and Setters

    public String getHtmlId() {
        return htmlId;
    }

    public void setHtmlId(String htmlId) {
        this.htmlId = htmlId;
    }

    public String getHtmlClass() {
        return htmlClass;
    }

    public void setHtmlClass(String htmlClass) {
        this.htmlClass = htmlClass;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    //----------------------------------------------------------------------- Instance Methods


    /**
     * Implementations should return the HTML for the element
     *
     * @return a string representation of the object.
     */
    public abstract String toString();
}
